/* *
 * @file    hal_adc.c
 * @author  Azolla (1228449928@qq.com)
 * @brief    
 * @version 0.1
 * @date    2022-09-19
 * 
 * @copyright Copyright (c) 2022
 * */
#include <string.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_continuous.h"

#include "hal_adc.h"

#if !CONFIG_ADC_AUDIO_ENABLE

#define  TAG   "hal_adc"
  
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define CONFIG_ADC_OUTPUT_TYPE         ADC_DIGI_OUTPUT_FORMAT_TYPE1
#else
#define CONFIG_ADC_OUTPUT_TYPE         ADC_DIGI_OUTPUT_FORMAT_TYPE2
#endif

static const adc_channel_t channel[] = { ADC_CHANNEL_9 };    // ESP32S3->IO10->ADC1_CH9
#define ADC_CHANNEL_NUM    (sizeof(channel) / sizeof(adc_channel_t))  // < SOC_ADC_PATT_LEN_MAX
#define ADC_CONV_SIZE       (128)
 
static const uint8_t ADC_START_EVENT = BIT0;   
static EventGroupHandle_t xEvent = NULL;  
static QueueHandle_t xQueue = NULL;

static bool IRAM_ATTR adc_conv_done_cb(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data)
{
    TaskHandle_t task_handle = (TaskHandle_t)user_data;
    BaseType_t mustYield = pdFALSE;
    // Notify that ADC continuous driver has done enough number of conversions
    vTaskNotifyGiveFromISR(task_handle, &mustYield);
    return (mustYield == pdTRUE);
}
 
static void adc_continuous_init(const adc_channel_t *channel, const uint8_t channel_num, adc_continuous_handle_t *out_handle)
{
    adc_continuous_handle_t handle = NULL;

    adc_continuous_handle_cfg_t adc_config = {
        .max_store_buf_size = 1024,
        .conv_frame_size = ADC_CONV_SIZE,
    };
    ESP_ERROR_CHECK(adc_continuous_new_handle(&adc_config, &handle));

    adc_continuous_config_t dig_cfg = {
        .sample_freq_hz = 16 * 1000,
        .conv_mode = ADC_CONV_SINGLE_UNIT_1,
        .format = CONFIG_ADC_OUTPUT_TYPE,
    };

    adc_digi_pattern_config_t adc_pattern[ADC_CHANNEL_NUM] = { 0 };  // SOC_ADC_PATT_LEN_MAX
    for (int i = 0; i < channel_num; i++) {
        adc_pattern[i].unit      = ADC_UNIT_1;
        adc_pattern[i].channel   = channel[i];// & 0x7;
        adc_pattern[i].atten     = ADC_ATTEN_DB_12;
        adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;
        #ifdef TAG
        ESP_LOGI(TAG, "adc_pattern[%d].atten is :%x", i, adc_pattern[i].atten);
        ESP_LOGI(TAG, "adc_pattern[%d].channel is :%x", i, adc_pattern[i].channel);
        ESP_LOGI(TAG, "adc_pattern[%d].unit is :%x", i, adc_pattern[i].unit);
        #endif
    }
    dig_cfg.pattern_num = channel_num;
    dig_cfg.adc_pattern = adc_pattern;
    ESP_ERROR_CHECK(adc_continuous_config(handle, &dig_cfg));
    *out_handle = handle;
}

static bool check_valid_data(const adc_digi_output_data_t *data)
{
#if (CONFIG_ADC_OUTPUT_TYPE != ADC_DIGI_OUTPUT_FORMAT_TYPE1)
    if (data->type1.channel >= SOC_ADC_CHANNEL_NUM(ADC_UNIT_1)) {
        return false;
    }
#else
    if (data->type2.channel >= SOC_ADC_CHANNEL_NUM(ADC_UNIT_1)) {
        return false;
    }
#endif
    return true;
}
 
void adc_read_start(void)
{
    if (xEvent == NULL) return;
    xEventGroupSetBits(xEvent, ADC_START_EVENT);  
}

void adc_read_stop(void)
{
   if (xEvent == NULL) return;
   xEventGroupClearBits(xEvent, ADC_START_EVENT);  
}

bool adc_read_buff(adc_val_t *buff, uint32_t wait_timeout)
{
    if (xQueueReceive(xQueue, buff, wait_timeout) == pdTRUE) {
        return true;
    }
    return false;
}

 
void adc_read_task(void *arg)
{
    adc_continuous_handle_t handle = (adc_continuous_handle_t)arg;

    esp_err_t ret;
    uint16_t total = 0;   // 转化数
    uint32_t ret_num = 0;    // 当前读取返回的数量
    uint8_t *result = adc_memory_malloc(ADC_CONV_SIZE);
    // TaskHandle_t task_handle = xTaskGetCurrentTaskHandle(); 
    while (true) {
 
        xEventGroupWaitBits(xEvent, ADC_START_EVENT, pdTURE, pdFALSE, portMAX_DELAY);  // pdTURE
 
        adc_continuous_start(handle);

        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);  // 等待转换完成

        uint32_t total, olen = 0; 
        for (total = 0; total < ADC_CONV_SIZE; ) {
            esp_err_t ret = adc_continuous_read(handle, &result[total], ADC_CONV_SIZE - total, &olen, ADC_MAX_DELAY);
            if (ret == ESP_OK || ret == ESP_ERR_INVALID_STATE) { // 转化太快
                total += olen;
            }
        }
        adc_continuous_stop(handle);  // start -> read -> stop 这样就不会出现 ESP_ERR_INVALID_STATE

        // ADC数据分析 
        adc_val_t val = { 0 };
        uint32_t adc_sum = 0; uint16_t adc_val = 0;
        for (uint16_t i = 0; i < total; i += SOC_ADC_DIGI_RESULT_BYTES) {
            adc_digi_output_data_t *p = (void*)&result[i];
            if (check_valid_data(p)) {
                #if (CONFIG_ADC_OUTPUT_TYPE != ADC_DIGI_OUTPUT_FORMAT_TYPE1)
                adc_val = p->type1.data;
                #else
                adc_val = p->type2.data;
                #endif
            }
            adc_sum += adc_val;   // 累计求和
            val.buff[val.size++] = adc_val;
            #ifdef TAG
            //printf("%d\n", adc_val);  // VOFA+ 绘制波形
            #endif
            // ESP_LOGI(TAG, "Unit: %d, Channel: %d, Value: %d", p->type2.unit + 1, p->type2.channel, p->type2.data);
        } 

        total = total / SOC_ADC_DIGI_RESULT_BYTES;
        val.average = adc_sum / total + 1;  // 求平均数
        val.volt    = (adc_sum) * 284 / 4095;  // ADC参考电压2.84V（ADC_ATTEN_DB_11）
        if (bat_volt_value > 10) bat_volt_value += 1;  // 误差补偿
        #ifdef TAG
        ESP_LOGI(TAG, "adc_sum[%d]-> ADC: %ld;  Volt: %d", total, val.average, val.volt);
        #endif
        xQueueSend(xQueue, &val, (TickType_t)0);
    }
    vTaskDelete(NULL);
}
 

void hal_adc_init(void)
{
    adc_continuous_handle_t handle = NULL;
    adc_continuous_init(channel, sizeof(channel) / sizeof(adc_channel_t), &handle);

    TaskHandle_t task_handle;
    xEvent = xEventGroupCreate();
    xQueue = xQueueCreate(2, sizeof(adc_val_t));
    xTaskCreatePinnedToCore(adc_read_task, "adc_read_task", 4 * 1024, handle, 7, &task_handle, PRO_CPU_NUM);  
 
    adc_continuous_evt_cbs_t cbs = {
        .on_conv_done = adc_conv_done_cb,
    };
    ESP_ERROR_CHECK(adc_continuous_register_event_callbacks(handle, &cbs, task_handle));
    // ESP_ERROR_CHECK(adc_continuous_start(handle));
#if 0  // test...
    while (1) {
        #if 0
        /* 定时采集一次BAT电压值 */
        static uint8_t adc_time = 0;
        if (++adc_time == 40) {  // 开启采集
            adc_read_start();
        } else if (adc_time > 50) {  // 采集完成
            adc_time = 0;
            uint16_t adc_volt = adc_read_value();
            ESP_LOGI(TAG, "adc_volt = %d", adc_volt);
        }
        vTaskDelay(100);
        #else
        vTaskDelay(5000);
        adc_read_start();
        #endif
    }
#endif
}

#endif
